/** 
 * Simplified Pascal in the EBNF:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations modules stmtblock  -- NAO EXISTE MAIS PROCEDURE DENTRO DE PROCEDURE
 * 
 * declarations -> 
 *    [CONST ID = constant';' {ID = constant';'}]
 *    [TYPE ID = type';' {ID = type';'}]
 *    [VAR idlist ':' type';' {idlist ':' type';'}]
 *    
 *
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparm';' declarations stmtblock ';'  -- NAO EXISTE MAIS PROCEDURE DENTRO DE PROCEDURE
 * 
 * function -> FUNCTION ID formalparm ':' typeid ';'declarations stmtblock';' -- NAO EXISTE MAIS PROCEDURE DENTRO DE PROCEDURE
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | forstmt | gotostmt | casestmt | idstmt
 * 
 * idstmt -> ID
 */

/** further simplifications for today only:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations stmtblock
 * 
 * declarations -> [VAR idlist ':' type';' {idlist ':' typeid';'}]
 *    modules
 *
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparm';' block ';'
 * 
 * function -> FUNCTION ID formalparm ':' typeid ';'block';'
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | idstmt | ""
 */

#include <stdio.h>
#include <string.h>

#include "parser.h"
#include "keywords.h"
#include "tokens.h"
#include "symtab.h"
#include "lexer.h"


int sympos;
int objtype;
int acctype,returnedtype;
int symbol_entry;
int errflag;
int offset;

FILE *target;


/* program -> PROGRAM ID ';' block '.'*/
void program(void)
{
  match(PROGRAM); 
  
  match(ID);
  match(';');
  block();
  match('.');  
}

/* block -> declarations stmtblock*/
void block(void)
{declarations();stmtblock();}
extern int symtab_nextentry;
/* declarations -> 
*    [VAR idlist ':' type';' {idlist ':' type';'}]
*    modules*/



void declarations(void)
{
  if(lookahead==VAR){
    match(VAR); /** symbol type 1: variable **/
rep_idlist:
    /** **/sympos=0;/** **/
    idlist(); /** produce one symbol list **/
    match(':');
    typeid(); /** to be used here **/
    match(';');
    if(lookahead==ID)goto rep_idlist;
  }
  modules();
  
}
 
/* modules -> {procedure | function} */
void modules(void)
{
  while(lookahead==PROCEDURE || lookahead==FUNCTION){
    if(lookahead==PROCEDURE)procedure();
    else function();
  }
}

/* procedure -> PROCEDURE ID formalparm';' block ';'*/
void procedure(void) 
{
  char symbolproc[32]; 
  match(PROCEDURE);/** symbol type 3: procedure **/
   strcpy(symbolproc,lexeme);
  match(ID);formalparm();match(';');
  block();
  match(';');
  if (symtab_lookup(symbolproc)){
	fprintf(stderr, "symbol \%s\" already declared\n",
	symbolproc);      
   }else{
	symtab_insert(symbolproc, 0, 3);
   }
   
}



/* formalparm -> '(' idlist ':' typeid {; idlist ':' typeid}  ')' ';' */
void formalparm(void){
  match('(');
  idlist(); match(':'); typeid();
  while(lookahead == ';'){
    match(';');
    idlist(); match(':'); typeid();
  }
  match(')'); match(';');
    
}


 /* function -> FUNCTION ID formalparm ':' typeid ';'block';'*/
void function(void)
{
  char symbolfunc[32];
  match(FUNCTION);/**symbol type 4: function **/
  strcpy(symbolfunc,lexeme);
  match(ID);  
  formalparm();match(':');
  int tmptype = 0;//typheck de procedimee(4);
  match(';');
  block();
  match(';');
  if (symtab_lookup(symbolfunc)){
	fprintf(stderr, "symbol \%s\" already declared\n",
	symbolfunc);
      
   }else{
	symtab_insert(symbolfunc, tmptype, 4);
   }
}

#define MAXSYMLIST 0x10000
#define MAXIDSIZE 32
char symlist[MAXSYMLIST][MAXIDSIZE];
int sympos;

/* idlist -> ID {','ID}*/
void idlist(void)
{
  /** **/strcpy(symlist[sympos++],lexeme);/** **/
  match(ID);
  while(lookahead==','){
    match(',');
    /** **/strcpy(symlist[sympos++],lexeme);/** **/
    match(ID);
  }
}

/*typeid -> INTEGER | REAL */
void typeid(void)
{
 /** datatype **/  int dtype, i; 
  switch(lookahead){
    case INTEGER:
      match(dtype=INTEGER);
      break;
    default:
      match(dtype=REAL);
  }
  
  /** */
  if(objtype == 1){
    
    for (i=0;i<sympos;i++){
      if (symtab_lookup(symlist[i])){
	fprintf(stderr, "symbol \%s\" already declared\n",
	symlist[i]);
      
      }else{
	symtab_insert(symlist[i],dtype, objtype);
      }
    }
  /** */
  }  
}

/* stmtblock -> BEGIN stmtlist END */
void stmtblock(void)
{
  match(BEGIN);stmtlist();match(END);
}

/* stmtlist -> stmt {';'stmt} */
void stmtlist(void)
{
  stmt();
  while(lookahead==';'){
    match(';');stmt();
  }
}

/* ifstmt -> IF expression THEN stmt[ELSE stmt]*/
void ifstmt(void)
{
  match(IF); expression(); match(THEN);
    stmt();
  if(lookahead==ELSE) {
    match(ELSE);stmt();
  }
}

/* whilestmt -> WHILE expression DO stmt */
void whilestmt(void)
{
  match(WHILE);expression();match(DO);
    stmt();
  
  }

/*repstmt -> REPEAT stmtlist UNTIL expression */
void repstmt(void)
{
  match(REPEAT);
  stmtlist();
  match(UNTIL);
  expression();
}

/*forstmt-> FOR assign [TO|DOWNTO] expression DO stmt */

void forstmt(void)
{
  match(FOR);
  assign();
  if (lookahead==TO)
    match(TO);
  else
    match(DOWNTO);
    expression();
    match(DO);
    stmt();
  
}
/* stmt -> stmtblock | ifstmt | whilestmt | repstmt | idstmt | "" */
void stmt(void)
{
  switch(lookahead){
    case BEGIN:
      stmtblock();
      break;
    case IF:
      ifstmt();
      break;
    case WHILE:
      whilestmt();
      break;
    case REPEAT:
      repstmt();
      break;
    case ID:
      idstmt();
      break;
  }
}

/*gotostm -> GOTO UINT*/ 
  
/*void gotostmt(void)
{
	match('GOTO'); match('UINT');
}*/

/*isrelationalop -> '=' | '>' | '=' | '>' | '<' | GEQ | LEQ | NEQ */
isrelationalop(token_t token)
{
  switch(token){
    case '=':
    case '>':
    case '<':
    case GEQ:
    case LEQ:
    case NEQ:
      return token;
  }
  return 0;
}

/* isaddop-> '+' |'-' | OR */
isaddop(token_t token)
{
  switch(token){
    case '+':
    case '-':
    case OR:
      return token;
  }
  return 0;
}

/* ismulop -> '*' | '/' | AND | DIV | MOD */ 
ismulop(token_t token)
{
  switch(token){
    case '*':
    case '/':
    case AND:
    case DIV:
    case MOD:
      return token;
  }
  return 0;
}

/* expression-> expr [isrelationalop expr]*/
void expression(void)
{
  /** an inherited attribute is get from the left side of an assignment if any **/
  /*OBS: se esta inteiro e aparceu ponto flutuante, quando fechar o parenteses tudo deve ser convertido a ponto flutuante. Se estiver pontu flutuante e aparecer inteiro dentro dos parenteses, manten-se normalmente os tipos*/
  
  
  expr();
  if(isrelationalop(lookahead)){ /* VAMOS ADMITIR QUE SEJA POSSIVEL COMPARAR INTEIRO COM PONTO FLUTUANTE*/
    match(lookahead); expr();  /*AQUI TERA QUE SER UMA VARIAVEL LOGICA*/
  }
  /** at this point we get a resulting data type as a synthesized attribute **/
}

/*expr-> [-] term {isaddop term} */
void expr(void)
{
  /** */int isnegate=0, op=0;/** */
  if(lookahead=='-'){
    /** */ isnegate=1;/** */
    
    match('-');
  }
  term();
  /** */ if(isnegate)fprintf(target, "\tnegl %%eax\n");/** */
  while(isaddop(lookahead)){
    /** */fprintf(target, "\tpushl %%eax\n"); op=lookahead;/** */
    match(lookahead); term();
    /** */ fprintf(target, "\t%s %%eax, (%%esp)\n", addopstr(op)); /** */
  }
  /** */ if(op) fprintf(target, "\tpopl %%eax\n"); /** */  /*caso não tenha tido operador*/
}

/* term-> factor {ismulop factor}*/
void term(void)
{
  /** */int op=0;/** */
  factor();
  while(ismulop(lookahead)){
    /** */fprintf(target, "\tpushl %%eax\n"); op=lookahead;/** */
    match(lookahead); factor();
    /** */
    fprintf(target, "\tpopl %%ecx\n");
    fprintf(target, "\tpushl %%eax\n");
    fprintf(target, "\tmovl %%ecx, %%eax\n");
    switch(op){
      case '*':
      fprintf(target, "\timull  (%%esp)\n");
      break;
      case '/':
      fprintf(target, "\tcltd \n");
      fprintf(target, "\tdivl (%%esp)\n");
      break;
    }
    /** */
  }
}

/* factor-> UINT | FLOAT | ID ['=' expr] |'(' exprlist ')'| NOT factor */
void factor(void){
  char symbolgen[32];
  int obj;
    switch(lookahead){
      case UINT:
	/** */
	switc(acctype = max(acctype, 1));{
        case 1:
	  fprintf(target, "\tmovl $%d, %%eax\n", lexeme/*(lexeme???)*/);
	  break;
	case 2:
	  fprintf(target, "\tmovl $%d,%%eax\n", ci2ieee(lexeme)/*(lexeme???)*/);
	  break;
	}
	/** */
	match(UINT);
	break;
      case FLOAT:
	/** **/
	acctype = max(acctype, 2);
        match(FLOAT);
	break;
      case ID:
	/**nesse contexto o simbulo deverá ser uma variavel ou uma funcao
	 * usando a symtab_lookup call**/
	
	/** **/
	if(symbol_entry = symtab_lookup(lexeme)==0){
          
	  fprintf(stderr, "emantic: symbol not found");
	  
	
	}else{
	  switch(symtab[symbol_entry][2]){
	    case 1:/** this is a variable **/
	    case 2:/** this is a function call**/
	      break;
	      
	    default:
	      fprintf(stderr, "symbol in ilegal FALAR QUAL A VARIAVEL TAMBÉM  context");  
	      errflag = FATALERR; /*(criar tabela de erros)*/
	      
	      
	  }
	}
	/** **/
	
	
	
	strcpy(symbolgen, lexeme);
	if ((obj= symtab_lookup(symbolgen)) == 0 ){
	  fprintf(stderr, "symbol \%s\" was not declared! Fatal Error!\n",
	  symbolgen);
      
	}else{
	  char symbol[256];
	  strcpy(symbol, lexeme);
	/** **/  
	match(ID);
	if(lookahead=='('){
	  /** this is the context for function call **/
	  
	  if(obj != 3 || obj != 4){
	    fprintf(stderr, "Sintaxe Error!\n");
	  }
          match('(');exprlist();match(')');
	  /** subroutine call is here **/
        }
        else{
	  /** this is the context for simple variable **/ /*SE A VARIAVEL FOR SIMPLES, GLOBAL TERA UM UNDESCORE NO FINAL DO NOME*/
	  /** **/
	  offset=symtab[symbol_entry][3];
	  if(offset == 0){
	    /** the variable is global , then its identification
	     * itself is the variable address **/
	    /** **/
	    fprintf(stdout, "\tmov %s_, %%eax\n",symbol); /*USAR ASSEMBLY CONVENCAO AT&T LINUX PARA PROCESSADOR INTEL */
	    
	    } else{
	      /** in this context all symbols are looked up in the symbol table in order to compose the stack address:
	        <offset>(%ebp)
	      for instance, a variable at offset -4 would be whitten as -4(%ebp)
	      a second variable with size 4 would be translated to -8(%ebp)
	      OBS: os offsets precisam serem calculados a medida em que a symtab vai sendo preenchida
	      on the other hand (por outro lado), a parameter in the first slot would be interpreted as 8(%ebp)"ENDERECO DE RETORNO ebp fica de 0 a 3, o primeiro endereco de retorno sera 8"
	      **/
	      /** **/
	      fprintf(stdout, "\tmov %d(%%ebp), %%eax\n",offset);
	      /** **/
	      }
	  
	  if(obj != 1 || obj != 2){
	    fprintf(stderr, "Sintaxe Error!\n");
	  }
	  
	}
        }
        break;
      case '(':
        match('(');
	/** **/returnedtype=0;/** **/
	expression();
	/** **/ acctype = max(acctype, returnedtype); /** **/
	match(')'); /*SE RETURNED TYPE FOR LÓGICO, O ACCTYPE SO PODE SER LOGICO*/
	/** at this point, expression has synthesized an attribute storing resulting data type in the expression. However, the just calculated attribute might be grater than the Left-attribute along the current expression, so that a promotion must accur (tipo int é menos complexo que ponto flutuante) 
	 ISSO VAI ACOTECER TAMBÉM PARA INTEIRO SEM SINAL, "PROMOCAO DE TIPO" **/
      break;
      default:
        match(NOT);factor(); /*PRECISA DE PONTO FLUTUANTE, SE APARECER UMA EXPRESSAO ARITMETICA NÃOPODE AFIRMAR NADA, SO NO FINL DO FATOR SABEREMOS SE O TIPO RETORNADO É LOGICO*/
    }
}

/*exprlist-> expression {',' expression}*/
void exprlist(void)
{
  /** is the context for passing paramters to procedures or function 
   * "printf no local de saída com PUSCH %EAX no caso do processoador intel"**/
  expression();
  /** expression result stored on accumulator **/
  /** push the accumulator onto the stack **/
  /** **/fprintf(stdout, "\tpush %%EAX\n"); /** **/
  
  while(lookahead==','){
    match(',');expression();
 
    /** expression result stored on accumulator **/
    /** push the accumulator onto the stack **/
  }
}
